package net.onthewings.stk
{
	/***************************************************/
	/*! \class DelayL
	    \brief STK linear interpolating delay line class.
	
	    This Delay subclass implements a fractional-
	    length digital delay-line using first-order
	    linear interpolation.  A fixed maximum length
	    of 4095 and a delay of zero is set using the
	    default constructor.  Alternatively, the
	    delay and maximum length can be set during
	    instantiation with an overloaded constructor.
	
	    Linear interpolation is an efficient technique
	    for achieving fractional delay lengths, though
	    it does introduce high-frequency signal
	    attenuation to varying degrees depending on the
	    fractional delay setting.  The use of higher
	    order Lagrange interpolators can typically
	    improve (minimize) this attenuation characteristic.
	
	    by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
	*/
	/***************************************************/
	public class DelayL extends Delay
	{
		//! Default constructor creates a delay-line with maximum length of 4095 samples and zero delay.
		//! Overloaded constructor which specifies the current and maximum delay-line lengths.
		/*!
			An StkError will be thrown if the delay parameter is less than
			zero, the maximum delay parameter is less than one, or the delay
			parameter is greater than the maxDelay value.
		 */	
		public function DelayL(...args):void {
			super();
			
			if (!args || args.length == 0){
				doNextOut_ = true;
			} else {
				var delay:Number = args[0];
				var maxDelay:Number = args[1];
				
				if ( delay < 0.0 || maxDelay < 1 ) {
					errorString_ = "DelayL::DelayL: delay must be >= 0.0, maxDelay must be > 0!";
					handleError( StkError.FUNCTION_ARGUMENT );
				}
			
				if ( delay > maxDelay ) {
					errorString_ = "DelayL::DelayL: maxDelay must be > than delay argument!";
					handleError( StkError.FUNCTION_ARGUMENT );
				}
			
				// Writing before reading allows delays from 0 to length-1. 
				if ( maxDelay > inputs_.length-1 ) {
					inputs_.length = maxDelay+1;
					this.clear();
				}
			
				inPoint_ = 0;
				this.setDelay(delay);
				doNextOut_ = true;
			}
		}
	
		//! Class destructor.
		public override function destruct():void {
			super.destruct();
		}
	
		//! Set the delay-line length.
		/*!
			The valid range for \e theDelay is from 0 to the maximum delay-line length.
		*/
		public override function setDelay(delay:Number):void {
			var outPointer:Number;

			if ( delay > inputs_.length - 1 ) { // The value is too big.
				errorString_ = "DelayL::setDelay: argument (" + delay + ") too big ... setting to maximum!";
				handleError( StkError.WARNING );
		
				// Force delay to maxLength
				outPointer = inPoint_ + 1.0;
				delay_ = inputs_.length - 1;
			} else if (delay < 0 ) {
				errorString_ = "DelayL::setDelay: argument (" + delay + ") less than zero ... setting to zero!";
				handleError( StkError.WARNING );
		
				outPointer = inPoint_;
				delay_ = 0;
			} else {
				outPointer = inPoint_ - delay;	// read chases write
				delay_ = delay;
			}
		
			while (outPointer < 0)
				outPointer += inputs_.length; // modulo maximum length
		
			outPoint_ = outPointer;	 // integer part
			if ( outPoint_ == inputs_.length ) outPoint_ = 0;
			alpha_ = outPointer - outPoint_; // fractional part
			omAlpha_ = 1.0 - alpha_;
		}
	
		//! Return the current delay-line length.
		public override function getDelay():Number {
			return delay_;
		}
	
		//! Return the value which will be output by the next call to tick().
		/*!
			This method is valid only for delay settings greater than zero!
		 */
		public override function nextOut():Number {
			if ( doNextOut_ ) {
				// First 1/2 of interpolation
				nextOutput_ = inputs_[outPoint_] * omAlpha_;
				// Second 1/2 of interpolation
				if (outPoint_+1 < inputs_.length)
					nextOutput_ += inputs_[outPoint_+1] * alpha_;
				else
					nextOutput_ += inputs_[0] * alpha_;
				doNextOut_ = false;
			}
		
			return nextOutput_;
		}
		
		protected override function computeSample( input:Number ):Number {
			inputs_[inPoint_++] = input;

			// Increment input pointer modulo length.
			if (inPoint_ == inputs_.length)
				inPoint_ = 0;
		
			outputs_[0] = nextOut();
			doNextOut_ = true;
		
			// Increment output pointer modulo length.
			if (++outPoint_ == inputs_.length)
				outPoint_ = 0;
		
			return outputs_[0];
		}
	
		protected var alpha_:Number;
		protected var omAlpha_:Number;
		protected var nextOutput_:Number;
		protected var doNextOut_:Boolean;
		
	}
}